﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Harkon.AppServer.cOne.Service;
using log4net;
using System.Diagnostics;
using System.Threading;
using System.ServiceModel.Description;
using Harkon.AppServer.cOne.Events;
using Harkon.AppServer.cOne.Service.Remote;

namespace Harkon.AppServer.cOne.Registry
{
    [ServiceBehavior(
        IncludeExceptionDetailInFaults = true,
        InstanceContextMode = InstanceContextMode.Single,
        ConcurrencyMode = ConcurrencyMode.Multiple
        )]
    public partial class Registry : AbstractService, IRegistry
    {

        private static ILog logger = LogManager.GetLogger(typeof(Registry));
        private Thread serviceHostThread;
        private ServiceHost serviceHost;
        public IRemoteCall RemoteCallService { get; set; }
        private string RegitryUri { get; set; }

        public Registry()
        {
            serviceHost = new ServiceHost(this);
        }

        #region IRegistry Members

        private IDictionary<string, IList<ComponentInformation>> serviceRegistry = new Dictionary<string, IList<ComponentInformation>>();

        public void RegisterComponent(ComponentInformation component)
        {
            if (!serviceRegistry.ContainsKey(component.Name))
            {
                serviceRegistry.Add(component.Name, new List<ComponentInformation>());
            }
            if (string.IsNullOrEmpty(component.Uri) || string.Equals(component.Uri, "local"))
                component.Uri = GetRegistryUri();
            if (!serviceRegistry[component.Name].Contains(component))
                serviceRegistry[component.Name].Add(component);
            logger.InfoFormat("Registered component {0}", component.ToString());
        }

        public bool IsComponentRegistered(ComponentInformation component)
        {
            bool existing = serviceRegistry.ContainsKey(component.Name);
            if (existing)
            {
                var localComponents = from existingComponent in serviceRegistry[component.Name]
                            where existingComponent.GetType() == typeof(ComponentInformation)
                            select existingComponent;
                if (localComponents.Count() == 1)
                    return true;
                var remoteComponents = from existingComponent in serviceRegistry[component.Name]
                                     where existingComponent.GetType() == typeof(RemoteComponentInformation)
                                     select existingComponent;
                if (remoteComponents.Count() >= 1)
                    return true;
            }
            return false;
        }

        public void UnregisterComponent(string componentName)
        {
            if (!serviceRegistry.ContainsKey(componentName))
            {
                throw new Exception("ComponentInformation is missing!");
            }
            serviceRegistry.Remove(componentName);
            logger.Info(string.Format("Unregistered component {0}", componentName));
        }

        public ComponentInformation[] GetRegisteredComponents()
        {
            List<ComponentInformation> allComponents = new List<ComponentInformation>();
            foreach (var namedComponents in serviceRegistry.Values)
            {
                allComponents.AddRange(namedComponents);
            }
            var localComponents = from localComponent in allComponents
                                  where
                                      localComponent.Uri.Equals(GetRegistryUri())
                                  select localComponent;
            return localComponents.ToArray<ComponentInformation>();
        }

        public ServiceName[] GetRegisteredServicesForComponent(ComponentInformation component)
        {
            var query = from localComponent in serviceRegistry[component.Name]
                        where localComponent.GetType() == typeof(ComponentInformation)
                        select localComponent;
            return query.First().Services.Keys.ToArray<ServiceName>();
        }

        public ComponentInformation GetRegisteredComponentsForName(ServiceName serviceName)
        {
            string componentName = serviceName.Component;
            if (!serviceRegistry.ContainsKey(componentName))
                throw new Exception("No component know with name " + componentName);

            //first get local component
            var query = from localComponent in serviceRegistry[componentName]
                        where
                            localComponent.GetType() == typeof(ComponentInformation)
                        select localComponent;
            if (query.Count() == 1)
                return query.First();

            //no local component => create remote component proxy
            query = from remoteComponent in serviceRegistry[componentName]
                    where
                        remoteComponent.GetType() == typeof(RemoteComponentInformation)
                    select remoteComponent;
            if (query.Count() != 1)
                return null;
            return query.First();
            
        }

        public void RegisterService(ServiceName service, object target)
        {
            if (!serviceRegistry.ContainsKey(service.Component))
            {
                throw new Exception("ComponentInformation is missing!");
            }
            ComponentInformation component = GetRegisteredComponentsForName(service);
            component.AddService(service, target);
            logger.Info(string.Format("Registered service {0} in component {1}", service, component));
        }



        public ComponentInformation GetComponentForService(ServiceName service)
        {
            if (!serviceRegistry.ContainsKey(service.Component))
            {
                throw new Exception();
            }

            return GetRegisteredComponentsForName(service);
        }

        public ServiceName GetCorrectServiceName(ServiceName serviceName)
        {
            if (!serviceRegistry.ContainsKey(serviceName.Component))
            {
                throw new Exception();
            }

            ComponentInformation currentInformation = GetRegisteredComponentsForName(serviceName);
            var query = from neededService in currentInformation.Services.Keys where neededService.Contract.Equals(serviceName.Contract) select neededService;
            ServiceName ret = query.First();
            return ret;
        }

        public object GetService(ServiceName service)
        {
            //TODO: Check remote existence
            ComponentInformation currentInformation = GetRegisteredComponentsForName(service);
            ServiceName ret = GetCorrectServiceName(service);
            return currentInformation.Services[ret];
        }

        public string GetServiceUri(ServiceName service)
        {
            return null;
        }

        public string GetRegistryUri()
        {
            return serviceHost.BaseAddresses[0].AbsoluteUri;
        }

        #endregion

        #region ILifecycleAware Members

        public override bool Init()
        {
            if (base.Init())
            {
                serviceHostThread = new Thread(Run);
                serviceHostThread.Name = "Registry";
                serviceHostThread.IsBackground = true;
                logger.InfoFormat("The registry service host url={0}", serviceHost.BaseAddresses[0].AbsoluteUri);
                logger.Debug("Registry initialized");
                return true;
            }
            return false;
        }

        public override bool Start()
        {
            if (base.Start())
            {
                serviceHostThread.Start();
                logger.Debug("Registry running");
                return true;
            }
            return false;
        }

        public override bool Stop()
        {
            if (base.Stop())
            {
                lock (this)
                {
                    Monitor.Pulse(this);
                }
                logger.Debug("Registry stopped");
                return true;
            }
            return false;
        }

        #endregion

        #region ServiceHost related

        private void Run()
        {
            //start service host
            try
            {
                serviceHost.Open();
                logger.Info("The registry service host is up and running.");
                lock (this)
                {
                    Monitor.Wait(this);
                }

                logger.Warn("The registry service host is closing...");
                serviceHost.Close();
                logger.Warn("The registry service host is closed!");
            }
            catch (CommunicationException ce)
            {
                logger.Fatal(ce.Message, ce);
                serviceHost.Abort();
            }
        }

        #endregion


    }
}
